import time
import hashlib
import hmac
import base64
import logging

def sign(salt, value, separator='|'):
    """Signs and timestamps a value so it cannot be forged.

        salt: bytes
        value: str
    """
    timestamp = str(int(time.time()))
    value = base64.b64encode(value.encode('utf8')).decode('utf8')
    signature = get_hmac(salt, value + timestamp)
    value = separator.join([value, timestamp, signature])
    return value

def unsign(salt, value, timeout=31*86400, separator='|'):
    """Returns the given signed value if it validates, or None.
    """
    return unsign2(salt, value, timeout, separator)[0]

def unsign2(salt, value, timeout=31*86400, separator='|'):
    if not value:
        raise ValueError('Invalid empty value.')
    
    parts = value.split(separator)
    if len(parts) != 3: 
        raise ValueError('Invalid value: {}'.format(value))

    signature = get_hmac(salt, parts[0] + parts[1])
    if parts[2] != signature:
        raise ValueError("Invalid signature: {}".format(value))

    timestamp = int(parts[1])
    if timeout and timestamp < time.time() - timeout:
        raise ValueError("Expired: {}".format(value))

    try:
        return base64.b64decode(parts[0].encode('utf8')).decode('utf8'), timestamp
    except:
        raise ValueError('Invalid value: {}'.format(value))


def get_hmac(key, value):
    return hmac.new(key, 
                msg=value.encode('latin-1'), 
                digestmod=hashlib.md5
            ).hexdigest()


def drop_privileges(uid_name='nobody', gid_name='nogroup'):
    '''if called before socket.listen, then socket can't listen on port numbers below 1024.
    walkaround: iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 1300
    '''
    import os, pwd, grp
    if os.getuid() != 0:
        # We're not root so, like, whatever dude
        return

    # Get the uid/gid from the name
    running_uid = pwd.getpwnam(uid_name).pw_uid
    running_gid = grp.getgrnam(gid_name).gr_gid

    # Remove group privileges
    os.setgroups([])

    # Try setting the new uid/gid
    os.setgid(running_gid)
    os.setuid(running_uid)

    # Ensure a very conservative umask
    old_umask = os.umask(0o77)

